home *** CD-ROM | disk | FTP | other *** search
- /*
- * SecsToRFC1123Date()
- * RFC1123DateToSecs()
- *
- * In this, we use the system international routines to convert between RFC 1123
- * (and RFC 822) compliant date/time strings and numeric dates and times. Requires
- * accompanying file with 'itl0', 'itl1', 'itl4', 'FMAT', and 'ZON#' resources.
- *
- * © Pete Resnick, 11 January 1994. Feel free to use these routines, but give me
- * proper credit in documentation if you do. I can be reached on the Internet
- * at the address "resnick@uiuc.edu".
- *
- * The 'FMAT' resource was created with version 1.0.1 of Michael Hecht's FMAT Editor
- * for ResEdit, © 1993. It is available from lots of anonymous FTP archives.
- *
- * Changes:
- * --------
- * 12 February 1994: Added StripString to strip comments and whitespace out.
- * Note that StripString doesn't use nice resources, etc.
- * 17 March 1994: Added GetFormatResources and 'zon#' handling.
- * 4 September 1994: Changed to use 'ZON#' resource.
- */
-
- #include <Errors.h>
- #include <Memory.h>
- #include <Resources.h>
- #include <SANE.h>
- #include <TextUtils.h>
-
- /* Nice ANSI prototypes */
- OSErr SecsToRFC1123Date(LongDateTime *time, long gmtOffset, StringPtr string);
- StringToDateStatus RFC1123DateToSecs(StringPtr string, LongDateTime *time);
- void StripString(StringPtr fromString, StringPtr toString);
- OSErr GetFormatResources(NumFormatString ***numFormat, Handle *itlHandle, long *tOffset);
- void InitItlRsrcs(short *verbPtr, short *idPtr);
- void ResetItlRsrcs(short *verbPtr, short *idPtr);
-
- /*
- * Define the resource ID of the 'itl0', 'itl1', 'itl4', 'FMAT', and 'ZON#' resources
- * for RFC 1123 dates.
- */
- #define INTL_RESOURCES 128
-
- /* Create new MachineLocation so there's no need to sign extend by hand */
- typedef struct {
- Fract latitude;
- Fract longitude;
- struct {
- long dlsDelta : 8;
- long gmtDelta : 24;
- } gmtFlags;
- } NewMachineLocation;
-
- /* Structure in 'ZON#' resource. The Pascal string is packed and even-padded. */
- typedef struct {
- short zoneMinutes;
- Str255 zoneName;
- } *ZoneInfoPtr;
-
- /* Convert time to string */
- OSErr SecsToRFC1123Date(time, gmtOffset, string)
-
- LongDateTime *time; /* Time to convert, nil if current time and PRAM offset from GMT */
- long gmtOffset; /* Offset in seconds from GMT */
- StringPtr string; /* String to return */
-
- {
- LongDateCvt theTime; /* Record for current time */
- StringPtr tempString; /* Temporary string */
- Handle itlHandle; /* Handle for 'itl4' resource */
- long tOff, tLen; /* Offset and length for international table */
- NumFormatStringRec **numFormat; /* Resource containing time zone format */
- FormatStatus fStatus; /* Return status of time zone formatting */
- short smVerbs[3], curIDs[3]; /* Holders for current Script Manager settings */
- short gmtHundred; /* Offset from GMT in "hundred hours" format */
- extended80 gmtHundred80; /* Extended for conversion */
- double_t gmtHundred96; /* Extended for conversion */
- OSErr errCode; /* Return code */
-
- /* Get the current time and PRAM offset from GMT if the time passed was nil. */
- if(time == nil) {
- NewMachineLocation loc;
-
- ReadLocation((MachineLocation *)&loc);
- gmtOffset = loc.gmtFlags.gmtDelta;
-
- theTime.hl.lHigh = 0L;
- errCode = ReadDateTime((unsigned long *)&theTime.hl.lLow);
- if(errCode != noErr)
- return errCode;
- time = &theTime.c;
- }
-
- /* First set up to use our international resources */
- InitItlRsrcs(smVerbs, curIDs);
-
- /* Convert time and date to RFC 1123 format string */
- LongDateString(time, abbrevDate, string, nil);
- tempString = string + (*string + 1);
- LongTimeString(time, true, tempString, nil);
- *string += *tempString + 1;
- *tempString = ' ';
- tempString = string + (*string + 1);
-
- /* Get the resources to format the time zone */
- errCode = GetFormatResources(&numFormat, &itlHandle, &tOff);
-
- if(errCode == noErr) {
-
- /*
- * Offset from GMT is going to be in the format "±hhmm", so make gmtHundred
- * 100 times number of hours plus number of minutes.
- */
- gmtHundred = gmtOffset / 60;
- gmtHundred %= 6000;
- gmtHundred = (gmtHundred % 60) + ((gmtHundred / 60) * 100);
-
- gmtHundred96 = gmtHundred;
- x96tox80(&gmtHundred96, &gmtHundred80);
-
- /* Format the offset from GMT into a string */
- if((FormatResultType)ExtendedToString(&gmtHundred80, *numFormat, (NumberParts *)(*itlHandle + tOff), tempString) != fFormatOK) {
- errCode = paramErr;
- } else {
- *string += *tempString + 1;
- *tempString = ' ';
- }
- }
-
- /* Reset the international resources to what they were before */
- ResetItlRsrcs(smVerbs, curIDs);
- return errCode;
- }
-
- /* Convert string to time. See Script Manager docs for return values */
- StringToDateStatus RFC1123DateToSecs(string, time)
-
- StringPtr string; /* String to convert */
- LongDateTime *time; /* Time to return */
-
- {
- long uLen, sLen; /* Used and current string length */
- DateCacheRecord dateCache; /* Cache for String2Date and String2Time */
- LongDateRec dateTime; /* Record to hold date and time */
- short smVerbs[3], curIDs[3]; /* Holders for current Script Manager settings */
- short gmtMinutes; /* offset in minutes from GMT */
- StringToDateStatus status; /* Return value */
- Handle itlHandle; /* Handle for 'itl4' resource */
- Handle zoneHandle; /* Handle for time zone info */
- ZoneInfoPtr zonePtr; /* Pointer for time zone info */
- long tOff; /* Offset for international table */
- NumFormatStringRec **numFormat; /* Resource containing time zone format */
- FormatStatus fStatus; /* Return status of time zone formatting */
- extended80 gmtHundred80; /* Offset from GMT in ±hhmm */
- double_t gmtHundred96; /* Extended for conversion */
- Str255 tempString; /* Holder for uncommented string */
-
- /* First initalize cache for String2Date and String2Time */
- if(InitDateCache(&dateCache) != noErr)
- return cantReadUtilities;
-
- /* Then set up to use our international resources */
- InitItlRsrcs(smVerbs, curIDs);
-
- /* Remove all comments and extra white space from the string */
- StripString(string, tempString);
- string = tempString;
-
- /* Get the current length of the string */
- sLen = *string++;
-
- /* Convert the date part of the string to a LongDateRec */
- status = StringToDate((Ptr)string, sLen, &dateCache, &uLen, &dateTime);
- if(!(status & fatalDateTime)) {
- string += ++uLen;
- sLen -= uLen;
-
- /* Convert the time part of the string to a LongDateRec */
- status = StringToTime((Ptr)string, sLen, &dateCache, &uLen, &dateTime);
- }
-
- /* If no errors, deal with the time zone part of the string */
- if(!(status & fatalDateTime) && (sLen != 0)) {
- string += uLen;
- sLen -= uLen + 1;
- *string = sLen;
-
- /* Initialize the time zone offset to zero */
- gmtMinutes = 0;
-
- /* It could be in "±hhmm" format if the length is 5 */
- if(sLen == 5L) {
-
- /* Get the resources to format the offset from GMT */
- if(GetFormatResources(&numFormat, &itlHandle, &tOff) == noErr) {
-
- /* Convert the string to an extended number */
- fStatus = StringToExtended(string, *numFormat,
- (NumberParts *)(*itlHandle + tOff),
- &gmtHundred80);
-
- /* Clear the error portion of status */
- status &= 0x00FF;
-
- /* If there are extra characters, still use the value */
- if((FormatResultType)fStatus == fSpuriousChars) {
- status |= extraneousStrings;
- fStatus = fFormatOK;
- }
-
- if((FormatResultType)fStatus == fFormatOK) {
- /* Convert the ±hhmm to minutes */
-
- x80tox96(&gmtHundred80, &gmtHundred96);
- gmtMinutes = gmtHundred96;
-
- gmtMinutes = (gmtMinutes % 100) + ((gmtMinutes / 100) * 60);
- status &= ~leftOverChars;
- } else {
- status |= tokenErr;
- }
- } else {
- status |= cantReadUtilities;
- }
- }
-
- /* If it wasn't in "±hhmm" format, see if it's in the zone list resource */
- if((sLen != 5) || ((StringToDateStatus)(status & tokenErr) == (StringToDateStatus)tokenErr)) {
-
- /* Get the zone list resource */
- zoneHandle = GetResource('ZON#', INTL_RESOURCES);
- if(zoneHandle == nil) {
- status |= cantReadUtilities;
- } else {
-
- /* Get the size of the zone list */
- sLen = InlineGetHandleSize(zoneHandle);
- zonePtr = (ZoneInfoPtr)*zoneHandle;
-
- /* Look at each entry in the zone list */
- while(zonePtr < (ZoneInfoPtr)(*zoneHandle + sLen)) {
-
- /* If the zone names are the same, use that gmtMinutes value */
- if(EqualString(string, zonePtr->zoneName, false, true)) {
- gmtMinutes = zonePtr->zoneMinutes;
- status &= ~leftOverChars;
- break;
- }
-
- /* Advance zonePtr to the next entry */
- zonePtr = (ZoneInfoPtr)((Ptr)zonePtr + sizeof(short) + zonePtr->zoneName[0]);
-
- /* Make sure zonePtr is word-aligned; the entries are padded */
- if((long)zonePtr & 1) {
- zonePtr = (ZoneInfoPtr)((Ptr)zonePtr + 1);
- }
- }
- }
- }
- }
-
-
- if(!(status & fatalDateTime)) {
-
- /* Add any offset from GMT in minutes */
- dateTime.ld.minute += gmtMinutes;
-
- /* Convert the record to the time */
- LongDateToSeconds(&dateTime, time);
- }
-
- /* Reset the international resources to what they were before */
- ResetItlRsrcs(smVerbs, curIDs);
- return status;
- }
-
- /*
- * Strip out all comments and extra white space from the string. This routine has
- * the comment, escape and quote characters hardcoded instead of being in resources.
- * If we were going hog-wild with the international routines, we could use the
- * IntlTokenize() routine to do this. In the interest of code size, and because
- * IntlTokenize() can't handle nested comments, we do it the quick and dirty way.
- */
- void StripString(fromString, toString)
-
- StringPtr fromString;
- StringPtr toString;
-
- {
- short sLen; /* The length of the string left to uncomment */
- short quoteCount; /* The number of open quotes or comments */
- short escaped; /* Flag to indicate escaped: 1 means not escaped */
- StringPtr fillString; /* A temporary pointer to the destination string */
- Boolean lastWhite; /* Flag to indicate the last character was whitespace */
- Byte endChar; /* The close quote or comment character */
-
- /* Get the length of the string */
- sLen = *fromString++;
-
- fillString = toString + 1;
- lastWhite = false;
- quoteCount = 0;
- while(sLen > 0) {
-
- /* Start out with no escape */
- escaped = 1;
-
- switch (endChar = *fromString) {
-
- case '(' :
- case ' ' :
- case ' ' :
-
- /* Use only one space for all contiguous whitespace and comments */
- if(!lastWhite) {
- *fillString++ = ' ';
- }
- lastWhite = true;
-
- /* Loop through until we get to the end of comments */
- do {
- switch(*fromString++) {
- case '(' :
- if(true) {
- quoteCount += escaped;
- } else {
- case ')' :
- quoteCount -= escaped;
- }
- default :
- escaped = 1;
- break;
- case '\\' :
- escaped ^= 1;
- }
- } while(--sLen > 0 && quoteCount > 0);
- break;
- case '[' :
- endChar = ']';
- case '"' :
- quoteCount = 1;
- default :
- do {
- if((*fillString++ = *fromString++) == '\\')
- escaped ^= 1;
- else
- escaped = 1;
- } while(--sLen > 0 && quoteCount > 0 &&
- (*fromString != endChar || escaped == 0));
- lastWhite = false;
- quoteCount = 0;
- }
- }
- *toString = fillString - (toString + 1);
- }
-
- OSErr GetFormatResources(numFormat, itlHandle, tOffset)
-
- NumFormatStringRec ***numFormat;
- Handle *itlHandle;
- long *tOffset;
-
- {
- OSErr errCode;
- long tLen;
- Byte hState;
-
- /* Initialize itlHandle to nil */
- *itlHandle = nil;
-
- /* Get the format resource for the offset from GMT */
- *numFormat = (NumFormatStringRec **)GetResource('FMAT', INTL_RESOURCES);
- if(*numFormat != nil) {
-
- /* Make sure we don't lose the numFormat in the process */
- hState = HGetState((Handle)*numFormat);
- HNoPurge((Handle)*numFormat);
-
- /* Get the correct number parts table from the 'itl4' resource */
- GetIntlResourceTable(smCurrentScript, smNumberPartsTable, itlHandle, tOffset, &tLen);
-
- /* Make numFormat purgeable again */
- HSetState((Handle)*numFormat, hState);
- }
-
- if(*itlHandle == nil) {
- errCode = ResError();
- return((errCode != noErr) ? errCode : resNotFound);
- }
- }
-
- void InitItlRsrcs(verbPtr, idPtr)
-
- short *verbPtr; /* Array of Script Manager verbs that are changed */
- short *idPtr; /* Array of original values */
-
- {
- short counter;
-
- *verbPtr++ = smScriptNumber; /* Verb for 'itl0' resource */
- *verbPtr++ = smScriptDate; /* Verb for 'itl1' resource */
- *verbPtr = smScriptToken; /* Verb for 'itl4' resource */
-
- verbPtr -= 2;
- for(counter = 3; --counter != -1; ) {
-
- /* Get the current setting and save it in the array of ID's */
- *idPtr++ = (short)GetScriptVariable(smCurrentScript, *verbPtr);
-
- /* Set to use our international resources */
- SetScriptVariable(smCurrentScript, *verbPtr++, INTL_RESOURCES);
- }
- ClearIntlResourceCache();
- }
-
- void ResetItlRsrcs(verbPtr, idPtr)
-
- short *verbPtr; /* Array of Script Manager verbs that have been changed */
- short *idPtr; /* Array of original values */
-
- {
- short counter;
-
- for(counter = 3; --counter != -1; ) {
-
- /* Set the Script Manager values back to what they were */
- SetScriptVariable(smCurrentScript, *verbPtr++, *idPtr++);
- }
- ClearIntlResourceCache();
- }